#include "udefcg.h"
#include "io.h"

/* 
 * Alokuje pole o poctu bloku v cg (blok = 8 fragmentu)
 */
int
alloc_blockap(GLBVAL *glb)
{	
	BLOCKARRS **blockap;
	int n;
	
	assert(glb->acg);
	
	n = (glb->acg)->cg_ndblk / afs.fs_frag;
	
	blockap = (BLOCKARRS **) malloc(n * sizeof(BLOCKARRS *));
	if (blockap == NULL) {
		warn("Can not alloc array for blocks info - blockap");
		return 1;
	}
		
	bzero(blockap, sizeof(BLOCKARRS *) * n);
	glb->blockap = (void **) blockap;
	
	return 0;
}


/*
 * Alokuje jednu polozku do pole bloku.
 * Vraci ji vynulovanou
 */
BLOCKARRS *
alloc_blockapitem(void)
{
	BLOCKARRS *blkarrp;
	
	blkarrp = (BLOCKARRS *) malloc (sizeof(BLOCKARRS));
	if (blkarrp == NULL) {
		warn("Can not alloc array for blocks info - blockapitem");
		return NULL;
	}
		
	bzero(blkarrp, sizeof(BLOCKARRS));
		
	return blkarrp;
}


/*
 * Alokuje jeden fragment do (pole) bloku.
 * Vraci ji vynulovanou
 */
FRAGARRS *
alloc_blockapfrag(void)
{
	FRAGARRS *fragarrp;
	
	fragarrp = (FRAGARRS *) malloc (sizeof(FRAGARRS));
	if (fragarrp == NULL) {
		warn("Can not alloc array for blocks info - blockapfrag");
		return NULL;
	}
		
	bzero(fragarrp, sizeof(FRAGARRS));
		
	return fragarrp;
}


/*
 * Uvolni celou pamet spojen s polem bloku blockap
 */
void
free_blockap(GLBVAL *glb)
{
	BLOCKARRS ** blockap;
	int n;
	int i;
	
	blockap = (BLOCKARRS **) glb->blockap;
	
	if (blockap) {
		n = (glb->acg)->cg_ndblk / afs.fs_frag;
		
		/* vsechny bloky uvolni */
		for (i = 0; i < n; i++) {
			if (blockap[i] != NULL)
				free_blockapitem (blockap[i]);
		}
		
		free (blockap);
		glb->blockap = NULL;
	}
}


/*
 * Uvolni jednotlive bloky z blockap
 */
void
free_blockapitem(BLOCKARRS *blkarrp)
{
	if (blkarrp->frag != NULL)
		free_blockapfrag(blkarrp->frag);
		
	free (blkarrp);
}


/*
 * Uvolni vsechny fragmenty v bloku z blockap
 */
void
free_blockapfrag(FRAGARRS *frag)
{
	FRAGARRS *head;
	FRAGARRS *item;
	
	/* vsechny fragmenty v bloku uvolni */
	head = frag;
	while (head) {
		item = head;
		head = head->frag;
		
		free (item);
	}
}

/*
 * pole je o velikosti obsazenych bloku (celkem)
 * tzn. pocet datovych (presunutelnych) bloku je o neco mensi
 */
int
alloc_slink(GLBVAL *glb)
{
	int n;
	size_t size;
	BLOCKARRS **slink;
	
	n = (glb->acg)->cg_ndblk / (afs.fs_frag);
	n = n - ((glb->acg)->cg_cs).cs_nbfree;
	
	size = n * sizeof(BLOCKARRS *);
	
	slink = (BLOCKARRS **) malloc(size);
	
	if (slink == NULL) {
		warn("Can not alloc array for blocks info - slink");
		return 1;
	}
		
	bzero (slink, size);
	glb->slink = (void **) slink;
	glb->slink_size = n;
	
	return 0;
}

/*
 * Uvolni slink. Je to jen pole ukazatelu 
 */
void
free_slink(GLBVAL *glb)
{
	BLOCKARRS **slink;
	
	slink = (BLOCKARRS **) glb->slink;
	
	if (slink) {
		free(slink);
		glb->slink = NULL;
	}
}

/* 
 * Cte inody (po blocich) ze zadane cg a plni pole
 */
int
fill_array(GLBVAL *glb)
{
	ino_t ino_start;		/* cislo prvniho inodu, ktery bude prohledavan */
	ino_t ino_stop;			/* cislo posledniho inodu */
	char st_ibuf[MAXBSIZE];	/* buffer do ktereho se nactou inody */
	char *ibuf;				/* ukazatek na buffer */
	ino_t ino;				/* index inodu */
	struct ufs1_dinode *dp1;
	int cylinder;
	int slinkpos = 0;		/* ukazovatko do slink */
	int ret;

	/* prvni / posledni inode */
	ino_start = 0;
	ino_stop = ino_start + (glb->acg)->cg_niblk;

	for (cylinder = 0; cylinder < afs.fs_ncg; cylinder++) {
		
		ibuf = st_ibuf;
		ret = read_inodes_block(glb, (void **) &ibuf, ino_start);
		if (ret)
			return 1;
		
		for (ino = ino_start; ino < ino_stop; ino++) {
			
			/* inody ctu po blocich o velikosti INOPB(fs) */
			if (ino_to_fsbo(&afs, ino) == 0) {
				ibuf = st_ibuf;
				ret = read_inodes_block(glb, (void **) &ibuf, ino);
				if (ret)
					return 1;				
			}
			
			/* XXX vyhledavani dalsich inodu pomoci bitmapy  */		
				
			dp1 = &(((struct ufs1_dinode *)ibuf)[ino_to_fsbo(&afs, ino)]);			
			if (dp1->di_blocks != 0)
				if (fill_array_inode(glb, ino, dp1, &slinkpos))
					return 1;
		}
		
		ino_start = ino_stop;
		ino_stop += (glb->acg)->cg_niblk;
			
	}
	
	return 0;
}

/*
 * Plni pole bloky z inodu dle kritrerii, nejprve direct, pak 1st indir, 2nd indir...
 */
int
fill_array_inode(GLBVAL *glb, ino_t ino, struct ufs1_dinode *dp, int *slinkpos)
{
	int st_ib1[MAXBSIZE];	/* buffery pro ib bloky */
	int st_ib2[MAXBSIZE];
	int st_ib3[MAXBSIZE];
	int *ib1;				/* pointry na buffery */
	int *ib2;
	int *ib3;
	int fblk;				/* prvni blok v cg */
	int lblk;				/* posledni blok v cg */
	int lastfrag = 0;		/* index do pole na posledni blok */
	int ret;
	int	blkindx = 0;
	int j,k;
		

	fblk = cgtod(&afs, (glb->acg)->cg_cgx);
	lblk = cgstart(&afs, (glb->acg)->cg_cgx + 1);

	/* direct block */
	ret = fill_array_dbib(glb, ino, dp->di_db, NDADDR, fblk, lblk, &blkindx, slinkpos);
	
	if (ret > 0)
		lastfrag = ret;
		
	if (ret == -1)
		lastfrag = 0;

	/* 
	 * XXX musim ukladat i samotne indir bloky pokud budu implementovat 
	 * presouvani i samotnych indir bloku ! 
	 */

	/* 1st indir block */
	if (dp->di_ib[0] != 0) {			
		ib1 = st_ib1;
		if (read_fs_block(glb, (void **)&ib1, dp->di_ib[0])) {
			fprintf(stderr, "Cylinder group scann error\n");
			return 1;
		}
			
		ret = fill_array_dbib(glb, ino, ib1, NINDIRS, fblk, lblk, &blkindx, slinkpos);
		
		
		if (ret > 0)
			lastfrag = ret;
			
		if (ret == -1)
			lastfrag = 0;
	}
	
	/* 2nd indir block */
	if (dp->di_ib[1] != 0) {
		ib1 = st_ib1;
		if (read_fs_block(glb, (void **) &ib1, dp->di_ib[1])) {
			fprintf(stderr, "Cylinder group scann error\n");
			return 1;
		}
			
		j=0;
		while (ib1[j] != 0) {
			ib2 = st_ib2;
			if (read_fs_block(glb, (void **) &ib2, ib1[j])) {
				fprintf(stderr, "Cylinder group scann error\n");
				return 1;
			}
				
			ret = fill_array_dbib(glb, ino, ib2, NINDIRS, fblk, lblk, &blkindx, slinkpos);			
			if (ret > 0)
				lastfrag = ret;
				
			if (ret == -1)
				lastfrag = 0;				
				
			j++;
		}
	}


	/* 3nd indir block */
	if (dp->di_ib[2] != 0) {
		ib1 = st_ib1;
		if (read_fs_block(glb, (void **) &ib1, dp->di_ib[2])) {
			fprintf(stderr, "Cylinder group scann error\n");
			return 1;
		}
			
		j=0;
		while (ib1[j] != 0) {
			ib2 = st_ib2;
			if (read_fs_block(glb, (void **) &ib2, ib1[j])) {
				fprintf(stderr, "Cylinder group scann error\n");
				return 1;
			}
				
			k = 0;
			while (ib2[k] != 0) {
				ib3 = st_ib3;
				if (read_fs_block(glb, (void **) &ib3, ib2[k])) {
					fprintf(stderr, "Cylinder group scann error\n");
					return 1;
				}
				
				ret = fill_array_dbib(glb, ino, ib3, NINDIRS, fblk, lblk, &blkindx, slinkpos);			
				
				if (ret > 0)
					lastfrag = ret;
					
				if (ret == -1)
					lastfrag = 0;			
					
				k++;
			}	
				
			j++;
		}
	}


	/* do posledniho fragmentu daneho inodu musim dat priznak */
	if (lastfrag != 0 && (glb->blockap)[lastfrag] != NULL)
		((BLOCKARRS **)(glb->blockap))[lastfrag]->flags = BLCKLAST;
		
	return 0;
}

/*
 * prepocet bloku na index do pole blockap
 */
uint
blktoindx (GLBVAL *glb, uint32_t blk)
{
	uint mod_rem;
	uint cylstart;
	uint indx;
	
	/* index do pole bloku */
	mod_rem = blk % afs.fs_frag;
	cylstart = cgstart(&afs, (glb->acg)->cg_cgx);
	indx = (blk - mod_rem - cylstart) / afs.fs_frag; 
	
	return indx;
}

/*
 * prepocet index do pole blockap do cisla fs bloku
 */
uint
indxtoblk (GLBVAL *glb, uint32_t indx)
{
	uint cylstart;
	uint blk;
	
	/* index do pole bloku */
	cylstart = cgstart(&afs, (glb->acg)->cg_cgx);
	blk = indx * afs.fs_frag + cylstart; 
	
	return blk;
}

/*
 * stara se o samotne zarazeni a alokaci bloku/fragmentu do pole na prislusne misto 
 * vraci:
 *  posledni obsazeny index v poli 
 *  0 pokud neni zadny fragment pouzit
 *  -1 fragmenty mimo tento cylinder
 */
int
fill_array_dbib(GLBVAL *glb, ino_t ino, int *blks, int count, int fblk, int lblk, int *blkindx, int *slinkpos)
{
	int i;
	int indx;
	int lastfrag = 0;
	struct cg *acg;
	BLOCKARRS *blkarrp;
	BLOCKARRS **blockap;
	BLOCKARRS **slink;
	FRAGARRS *fragarrp; 
	FRAGARRS *tmpfragp;	
	
	acg = glb->acg;
	blockap = (BLOCKARRS **) glb->blockap;
	slink = (BLOCKARRS **) glb->slink;
	
	
	for (i = 0; i < count && blks[i] != 0; i++, (*blkindx)++) {
		
		/* zkoumam jen bloky na danem cg */
		if (! (blks[i] >= fblk && blks[i] < lblk)) {
			lastfrag = -1;
			continue;
		}
		
		/* index do pole bloku */
		indx = blktoindx(glb, blks[i]);
		
		lastfrag = indx;
		
		/* neobsazeny blok */	
		if (blockap[indx] == NULL) {
			blkarrp = alloc_blockapitem();
			blkarrp->ino = ino;
			blkarrp->blkindx = *blkindx;
			blkarrp->apindx = indx;
			blkarrp->flags = BLCKALONE;
			blockap[indx] = blkarrp;
			
			/* do slinku pridam odkaz do blockap */
			assert ((*slinkpos) <= (glb->slink_size));
			slink[*slinkpos] = blockap[indx];
			(*slinkpos)++;
			
		}
		/* blok rozdelen na fragmenty */
		else {
			blkarrp->flags = BLCKNOMOVE; /* blok na frag nepresouvam */
			fragarrp = alloc_blockapfrag();
			fragarrp->ino = ino;
			fragarrp->fragoff = blks[i] % afs.fs_frag;
			fragarrp->blkindx = *blkindx;
			
			/* v poli bloku hledam posledni fragment (v bloku) */
			if (blockap[indx]->frag != NULL) {
				tmpfragp = blockap[indx]->frag;
				while (tmpfragp->frag != NULL)
					tmpfragp = tmpfragp->frag;
					
				tmpfragp->frag = fragarrp;					
			}
			else {
				blockap[indx]->frag = fragarrp;
			}			
		}
	}
	
	return lastfrag;
}

/*
 * z bitmapy zjistim, jaky bloky jsou plny/prazdny. projdu pole a pridam tam
 * bloky prazdny a nepresunutelny dle bitmapy.
 */
int
fill_array_restblocks(BLOCKARRS **blockap,  struct cg *acg)
{
	char *blkmap;
	int i,j;
	int sent;
	int lastused = 0;
	int mul;
	BLOCKARRS *blkarrp;
	
	blkmap = (char *) cg_blksfree(acg);

	sent = acg->cg_ndblk / afs.fs_frag;
	for (i = 0; i < sent; i++) {
		if (blockap[i] != NULL) {
			lastused = i;
			continue;
		}
		else {
			
			mul = i * afs.fs_frag;
			
			for (j = mul; j < mul + afs.fs_frag; j++) {
				if (isclr(blkmap, j)) {
					blkarrp = alloc_blockapitem();
					blkarrp->ino = 0;
					blkarrp->flags = BLCKNOMOVE;
					
					blockap[i] = blkarrp;
					break;
				}
			}
		}
	}
	
	return lastused;
}

/*
 * nastavuje cilovou pozici vsech bloku
 * kontroluje jestli jestli jsou jednotlive bloky presunutelne
 */
void
valid_blocks_order(GLBVAL *glb)
{
	int spos;		/* index do slink */
	int apindx;		/* index do blockap */
	int dstart;
	BLOCKARRS **slink;
	BLOCKARRS **blockap;
	
	slink = (BLOCKARRS **) glb->slink;
	blockap = (BLOCKARRS **) glb->blockap;
	
	
	/* zacatek datoveho bloku -> prepocet na index do blockap */
	dstart = cgdmin(&afs, (glb->acg)->cg_cgx);
	apindx = blktoindx(glb, dstart);

	for (spos = 0; (spos < glb->slink_size) && slink[spos]; spos++) {	
		/* jiz na svem miste */
		if (slink[spos] == blockap[apindx]) {
			slink[spos]->blkdest = slink[spos]->apindx;
			apindx++;
			continue;
		}		
		
		/* aktualni blok v slink je nepresunutelny, tzn ze je na svem miste */
		if (slink[spos]->flags & BLCKNOMOVE) {
			slink[spos]->blkdest = slink[spos]->apindx;
			continue;
		}
		/* posledni blok, takze je na svem miste  */
		if (slink[spos]->flags & BLCKLAST) {
			slink[spos]->blkdest = slink[spos]->apindx;
			continue;
		}
				
		/*  beru jako volne misto */
		if (blockap[apindx] == NULL) {
			slink[spos]->blkdest = apindx;
			apindx++;
		}
		/* volne misto */
		else if (blockap[apindx]->flags & BLCKFREE) {
			slink[spos]->blkdest = apindx;
			apindx++;
		}
		else if (blockap[apindx]->flags & BLCKALONE) {
			slink[spos]->blkdest = apindx;
			apindx++;
		}
		else {
			/* cilovy blok je nepresunutelny */
			if ((blockap[apindx]->flags & BLCKNOMOVE) ||
				(blockap[apindx]->flags & BLCKLAST)) {
				/* XXX */
				apindx++;
				spos--;
			}
		}
	}
	
#if DEBUG
	/*
	 *  VALID test
	 * Kontrola jestli umistuju bloky do skutecneho rozsahu cg
	 */
	int i, size;
	int last=0, apnum=0;
	
	/* vyhledam umisteni posledniho bloku */
	size = (glb->acg)->cg_ndblk / afs.fs_frag;
	for (i = 0; i < size; i++) {
		if (blockap[i] != NULL) {
			apnum++;
				
			if (i > last && blockap[i]->ino > 2)
				last = i;
		}
	}
	/* hledam blok, ktery se chce presunout za posledni pozici */
	for (i = 0; i < size; i++) {
		if (blockap[i] != NULL) {
			if (blockap[i]->blkdest > last) {
				fprintf(stderr, "ino: %d blkindx: %d; apindx: %d, blkdest: %d (last in cg: %d)\n", 
					blockap[i]->ino,
					blockap[i]->blkindx,
					blockap[i]->apindx, 
					blockap[i]->blkdest, last);
				fprintf(stderr, "number of blocks: %d\n", apnum);
			}
		}
	}
#endif
}

void
print_blockap(GLBVAL *glb)
{
	int 	nblocks;
	int 	i;
	FRAGARRS *fragarrp;
	BLOCKARRS **blockap;
	
	nblocks = (glb->acg)->cg_ndblk / afs.fs_frag;
	blockap = (BLOCKARRS **) glb->blockap;
	
	for (i = 0; i < nblocks; i++) {
		if (blockap[i] == NULL) {
			continue;			
		}
		
		printf("[%4d][%4d] block %4d: ", i, blockap[i]->blkdest, indxtoblk(glb, i));
		printf("%d", blockap[i]->ino);
		
		fragarrp = blockap[i]->frag;
		while (fragarrp != NULL) {
			printf(",%d", fragarrp->ino);
			fragarrp = fragarrp->frag;
		}
		printf(" (%d)", blockap[i]->blkindx);
		printf("\n");
		
	}
	
}

void 
print_bmap(struct cg *acg, int limit)
{
	int i;
	char *blkmap;
	int num;
	
	blkmap = (char *) cg_blksfree(acg);
	
	printf("\n");
	num = 0;
	for (i = 0; i < limit; i++) {
		
		if ((i%8)==0) {
			printf("\n %d:", num);
			num += 8;
		}
		
		if (isset(blkmap, i)) {
			printf("1");
		}
		else {
			printf("0");
		}
	}
	printf("\n");
}

void
print_slink(GLBVAL *glb)
{
	int i;
	BLOCKARRS ** slink;
	
	slink = (BLOCKARRS **) glb->slink;
	printf("== Slink ==\n");
	for (i = 0; i < glb->slink_size && slink[i]; i++) {
		printf("%d-", slink[i]->apindx);
	}
	
	printf("\n");
}


int
udefcg(GLBVAL *glb, int ncg)
{	
	if (alloc_blockap(glb))
		return 1;
		
	if (alloc_slink(glb))
		return 1;
		
	if (fill_array(glb))
		return 1;
			
	fill_array_restblocks((BLOCKARRS **)glb->blockap, glb->acg);
		
	valid_blocks_order(glb);
	
	return 0;
}
